﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using System.Web;
using System.Collections.Specialized;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;
using System.Net;
using System.Data;
using Talentnet.Core.Utilities.Cryptophy;
using Talentnet.Core.Utilities;
using System.Web.SessionState;

namespace Talentnet.Core.Web
{
    /// <summary>
    /// Basic web page if AS system
    /// </summary>
    /// <remarks>All pages of AS's website must inherit from this class</remarks>
    public abstract class SecurePage : System.Web.UI.Page
    {
        public interface ICustomLoadText
        {
            void OnLoadText(Control ctrl,TextResourceLoader res);
        }
        protected enum SecurePageType
        {
            None,
            Normal,
            Modal
        }
        DateTime _timeBeginRequest = DateTime.Now;
        HttpRequest _pageRequest;

        public SecurePage()
        {
            
        }
        #region properties

        
        /// <summary>
        /// Gets the secure query string.
        /// </summary>
        /// <value>The secure query string.</value>
        public NameValueCollection SecureQueryString
        {
            get
            {
                return _SecureQueryString;
            }
        }
        
        NameValueCollection _SecureQueryString = null;
        
        string _postData = null;
        public string PostData
        {
            get
            {
                if (_postData == null)
                {
                    System.IO.StreamReader post_stream = new System.IO.StreamReader(_pageRequest.InputStream);
                    _postData = post_stream.ReadToEnd();
                }
                return _postData;
            }

        }
        public bool IsRefreshPage
        {
            get
            {
                return _IsRefreshPage;

            }
        }
        bool _IsRefreshPage;
        public bool IsSecureQueryString
        {
            get
            {
                return Request["param"] != null;
            }
        }
        public bool IsStaticQueryString
        {
            get
            {
                return Request["sparam"] != null;
            }
        }
        bool _IsIntruderDetected;
        public bool IsNoCache
        {
            get
            {
                return _IsNoCache;
            }
            set
            {
                _IsNoCache = value;
            }
        }
        bool _IsNoCache = true;
        
        /// <summary>
        /// First page must be the login page to redirect: xxx_Login.aspx,xxx_ForceResetPassword.aspx
        /// </summary>

        protected string ValidPagesForForceResetPassword
        {
            get
            {
                return _ValidPagesForForceResetPassword.Trim(',');
            }
            set
            {
                _ValidPagesForForceResetPassword = "," + value.Replace(";", ",").Trim(',') + ",";
            }
        }
        string _ValidPagesForForceResetPassword;
        protected SecurePageType PageType
        {
            get
            {
                return _PageType;
            }
            set
            {
                _PageType = value;
            }
        }
        SecurePageType _PageType = SecurePageType.Normal;
        public abstract ICryptor Cryptor
        {
            get;
        }

        
        #endregion
        #region public method
        public bool IsUserWithPermission(string per_code)
        {
            if (HttpContext.Current.Session["UserPermissions"] == null) return false;
            string[] per_codes = (string[])HttpContext.Current.Session["UserPermissions"];
            return per_codes.FirstOrDefault<string>(obj => obj == per_code) != null;
        }

        public string BuildSecureQueryString(string query)
        {
            return "param=" + HttpUtility.UrlEncode(Cryptor.EncryptText(query, Session.SessionID.Substring(0, 8)));
        }
        public const string STATIC_KEY = "!*&@#^%$";
        public string BuildStaticQueryString(string query)
        {
            return "sparam=" + HttpUtility.UrlEncode(Cryptor.EncryptText(query, STATIC_KEY));
        }
        #endregion
        #region override methods
        protected override void OnInit(EventArgs e)
        {


            base.OnInit(e);
            if (IsSecureQueryString || IsStaticQueryString)
            {
                string encryptedQueryStr = "";
                if (IsSecureQueryString) encryptedQueryStr = Request["param"];
                else if (IsStaticQueryString) encryptedQueryStr = Request["sparam"];

                if (string.IsNullOrEmpty(encryptedQueryStr))
                {
                    _IsIntruderDetected = true;
                    RaiseIntruderEvent(IntruderType.SecureQueryString);
                }
                try
                {
                    string decryptedQueryStr = "";
                    if (IsSecureQueryString) decryptedQueryStr = Cryptor.DecryptText(encryptedQueryStr, Session.SessionID.Substring(0, 8));
                    else if (IsStaticQueryString) decryptedQueryStr = Cryptor.DecryptText(encryptedQueryStr, STATIC_KEY);
                    
                    if (decryptedQueryStr == "")
                    {
                        _IsIntruderDetected = true;
                        RaiseIntruderEvent(IntruderType.SecureQueryString);
                    }
                    else
                    {
                        _SecureQueryString = HttpUtility.ParseQueryString(decryptedQueryStr);
                    }
                }
                catch
                {
                    _IsIntruderDetected = true;
                    RaiseIntruderEvent(IntruderType.SecureQueryString);
                }
            }
            
            PageInitialize();
        }

        protected override void OnPreInit(EventArgs e)
        {
            base.OnPreInit(e);
            DoPagePreInit();

            if (_pageRequest == null) _pageRequest = Request;

            //force to redirect back to login page
            if (SessionForceChangePassword && _ValidPagesForForceResetPassword.IndexOf("," + Request.Url.Segments[Request.Url.Segments.Length - 1].ToLower() + ",") < 0)
            {
                Response.Redirect(LoginUrl);
                return;
            }

            CheckPagePermission();

            



        }
        protected override void RaisePostBackEvent(IPostBackEventHandler sourceControl, string eventArgument)
        {
            if ((this.Request.UrlReferrer == null)
                    || (this.Request.UrlReferrer != null && this.Request.UrlReferrer.PathAndQuery != this.Request.Url.PathAndQuery)
                    || (Session["sec_key_" + GetType().FullName] == null)
                    || (ViewState["sec_key"] == null)
                    || Session["sec_key_" + GetType().FullName].ToString() != ViewState["sec_key"].ToString()
                )
            {

                _IsIntruderDetected = true;
                RaiseIntruderEvent(IntruderType.CSRF);
                return;
            }
            base.RaisePostBackEvent(sourceControl, eventArgument);
        }
        protected override bool OnBubbleEvent(object sender, EventArgs e)
        {
            return _IsIntruderDetected;
        }
        string _serverIP = "";
        protected override void OnLoad(EventArgs e)
        {

            _serverIP = Request.ServerVariables["LOCAL_ADDR"];
            
            if (IsPostBack)
            {
                if (Session["sec_refresh"] == null) _IsRefreshPage = false;
                else if (ViewState["sec_refresh"] == null) _IsRefreshPage = false;
                else _IsRefreshPage = Session["sec_refresh"].ToString() != ViewState["sec_refresh"].ToString();
                if (!_IsRefreshPage) Session["sec_refresh"] = CryptoAgent.Current.MD5(DateTime.Now.Ticks.ToString());

            }
            else
            {
                Session["sec_refresh"] = CryptoAgent.Current.MD5(DateTime.Now.Ticks.ToString());
                ViewState["sec_key"] = Session["sec_key_" + GetType().FullName] = CryptoAgent.Current.SHA1(DateTime.Now.Ticks.ToString());
            }

            if (_IsIntruderDetected) return;
            base.OnLoad(e);
            switch (_PageType)
            {
                case SecurePageType.Normal:
                    ClientScript.RegisterStartupScript(GetType(), "sec_redirect_script", "if(window != parent.window) parent.window.location='" + EncodeHexJSString(Request.RawUrl) + "';", true);
                    break;
                case SecurePageType.Modal:
                    ClientScript.RegisterStartupScript(GetType(), "sec_redirect_script", "if(window == parent.window) parent.window.location='" + EncodeHexJSString(LoginUrl) + "';", true);
                    break;
            }


        }
        static string EncodeHexJSString(string s)
        {
            StringBuilder sb = new StringBuilder();
            foreach (char c in s)
            {
                sb.Append("\\x" + ((int)c).ToString("X"));

            }

            return sb.ToString();
        }
        protected override void OnPreLoad(EventArgs e)
        {
            WebPageTextLoader textLoader = new WebPageTextLoader(this);
            textLoader.LoadText();
            base.OnPreLoad(e);
        }
        protected override void OnPreRender(EventArgs e)
        {
            
            base.OnPreRender(e);
            if (_IsNoCache)
            {
                this.Response.AddHeader("Pragma", "no-cache");
                this.Response.AddHeader("Cache-Control", "no-store");
            }
            ViewState["sec_refresh"] = Session["sec_refresh"];
        }

        protected override void OnUnload(EventArgs e)
        {
            base.OnUnload(e);
            OnAspxWriteLog();
        }
        public override void ProcessRequest(HttpContext context)
        {
            if (_pageRequest == null) _pageRequest = context.Request;
            base.ProcessRequest(context);

        }
        #endregion
        #region abstract methods
        protected abstract void PageInitialize();
        protected abstract void OnAspxWriteLog();
        protected abstract void OnIntruderWriteLog();
        protected abstract void DoIntruderDetected(IntruderType type);
        #endregion
        #region private methods
        public string CurrentLanguage
        {
            get
            {
                WebPageTextLoader textLoader = new WebPageTextLoader(this);
                return textLoader.CurrentLanguage;
            }
            set
            {
                WebPageTextLoader textLoader = new WebPageTextLoader(this);
                textLoader.CurrentLanguage = value;
            }
        }
        public string LoginUrl
        {
            get
            {
                HttpCookie url_cokkie = HttpContext.Current.Request.Cookies["login_url"];
                if (url_cokkie != null)
                {
                    return url_cokkie.Value;
                }
                else
                {
                    return FormsAuthentication.LoginUrl;
                }
            }
            set
            {
                HttpCookie c = new HttpCookie("login_url", value);
                c.Path = Request.ApplicationPath;
                HttpContext.Current.Response.Cookies.Add(c);
            }
        }
        protected virtual void DoPagePreInit()
        {
        }
        public string LoadText(string sessionID, string key)
        {
            WebPageTextLoader loader = new WebPageTextLoader(this);
            return loader.LoadText(sessionID, key);
        }
        
        protected void CheckPagePermission()
        {
            System.Reflection.MemberInfo info = this.GetType();
            object[] attributes = info.GetCustomAttributes(true);
            for (int i = 0; i < attributes.Length; i++)
            {
                if (attributes[i] is PagePermission)
                {
                    PagePermission _accessPermission = (PagePermission)attributes[i];
                    string[] permissions = _accessPermission.PermissionName.Split(',');
                    bool hasPermission = false;
                    for (int j = 0; j < permissions.Length; j++)
                    {
                        if (IsUserWithPermission(permissions[j]))
                        {
                            hasPermission = true;
                        }
                    }
                    if (!hasPermission)
                    {
                        _IsIntruderDetected = true;
                        RaiseIntruderEvent(IntruderType.Permission);
                    }

                }
            }
        }

        private bool SessionForceChangePassword
        {
            get
            {
                if (Session["ForceChangePassword"] == null)
                    return false;
                else return (bool)Session["ForceChangePassword"];
            }
        }
        
        
        /*
        private string GetLocalIPs(string hostname)
        {

            IPHostEntry ips = Dns.GetHostByName(hostname);
            if (ips.AddressList.Length > 0)
            {
                return ips.AddressList[0].ToString();
            }
            return "";
        }
        */
        private void RaiseIntruderEvent(IntruderType type)
        {
            OnIntruderWriteLog();
            DoIntruderDetected(type);

        }
        #endregion
        internal class WebPageTextLoader
        {
            Control _ctrlContainer;
            private HttpSessionState Session
            {
                get
                {
                    return HttpContext.Current.Session;
                }

            }
            private string MapPath(string virtualPath)
            {
                return _ctrlContainer.Page.MapPath(virtualPath);
            }
            public WebPageTextLoader(Page container)
            {
                _ctrlContainer = container;
                
            }
            static Control FindParent(Control ctrl)
            {
                if (ctrl.Parent == null) return null;
                if (ctrl.Parent is Page || (ctrl.Parent is UserControl && !(ctrl.Parent is MasterPage))) return ctrl.Parent;
                return FindParent(ctrl.Parent);

            }
            static Control[] GetAllControls(Control root)
            {

                List<Control> list = new List<Control>();

                list.Add(root);
                if (root.HasControls())
                {
                    foreach (Control control in root.Controls)
                    {
                        list.AddRange(GetAllControls(control));
                    }
                }
                return list.ToArray();
            }
            public string LoadText(string sessionID, string key)
            {
                return TextResourceManager.GetText(sessionID, key);
            }
            public void LoadText()
            {
                Control[] allCtrls = GetAllControls(_ctrlContainer);
                string sessionText = "";
                for (int i = 0; i < allCtrls.Length; i++)
                {
                    Control parent = FindParent(allCtrls[i]);
                    if (parent != null)
                    {
                        sessionText = GetTextSession(parent);
                        TextResourceManager.SetSession(sessionText);
                        if (sessionText == "") continue;
                        if (allCtrls[i].ID != null)
                        {
                            if (allCtrls[i] is Literal)
                            {
                                ((ITextControl)allCtrls[i]).Text = TextResourceManager.GetText(allCtrls[i].ID);
                            }
                            else if (allCtrls[i] is CheckBox)
                            {
                                ((CheckBox)allCtrls[i]).Text = TextResourceManager.GetText(allCtrls[i].ID);
                            }
                            else if (allCtrls[i] is HyperLink)
                            {
                                ((HyperLink)allCtrls[i]).Text = TextResourceManager.GetText(allCtrls[i].ID);
                            }
                            else if (allCtrls[i] is IButtonControl)
                            {
                                ((IButtonControl)allCtrls[i]).Text = TextResourceManager.GetText(allCtrls[i].ID);
                            }
                            if (_ctrlContainer is ICustomLoadText)
                            {
                                ((ICustomLoadText)_ctrlContainer).OnLoadText(allCtrls[i], TextResourceManager);
                            }
                        }
                    }
                }
            }
            private string GetTextSession(Control ctrl)
            {
                System.Reflection.MemberInfo info = ctrl.GetType();
                object[] attributes = info.GetCustomAttributes(true);
                for (int i = 0; i < attributes.Length; i++)
                {
                    if (attributes[i] is PageTextResource)
                    {
                        PageTextResource obj = (PageTextResource)attributes[i];
                        return obj.ResourceKey;
                    }
                }
                return "";
            }
            private TextResourceLoader TextResourceManager
            {
                get
                {
                    if (Session["TextResourceManager"] == null)
                    {
                        Session["TextResourceManager"] = new TextResourceLoader(MapPath("~/App_Data/"), CurrentLanguage);
                    }
                    return (TextResourceLoader)Session["TextResourceManager"];
                }

            }

            public string CurrentLanguage
            {
                get
                {
                    if (Session["CurrentLanguage"] == null)
                    {
                        Session["CurrentLanguage"] = "vn";

                    }
                    return (string)Session["CurrentLanguage"];
                }
                set
                {
                    Session["CurrentLanguage"] = value;
                    TextResourceManager.SwitchLanguage(value);
                }
            }
        }
    }

    
}
